package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.constant.EsNormalConstant;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.pojo.dto.FormDataUpdateGetDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentCorrectionErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentRelatedErrorCodeEnum;
import com.xbongbong.pro.fundfixdata.pojo.dto.FundFixDataDTO;
import com.xbongbong.pro.fundfixdata.pojo.vo.FundFixDataVO;
import com.xbongbong.pro.purchase.pojo.dto.PurchaseInvoiceRepairAmountDTO;
import com.xbongbong.pro.script.pojo.dto.RepairInvoiceAmountDTO;
import com.xbongbong.pro.script.pojo.dto.UpdateDataDTO;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.domain.entity.PayBalanceEntity;
import com.xbongbong.saas.domain.entity.PaymentBalanceEntity;
import com.xbongbong.saas.domain.entity.PurchaseInvoiceRelationshipEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.PayPlanEntityExt;
import com.xbongbong.saas.domain.entity.ext.PayPlanSheetEntityExt;
import com.xbongbong.saas.domain.entity.ext.PaymentEntityExt;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.domain.entity.ext.PurchaseEntityExt;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.PayPlanEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.dictionary.FundFixTagEnum;
import com.xbongbong.saas.help.SaasSaveHelp;
import com.xbongbong.saas.help.workflow.PayAndPaymentSheetHelp;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.PayBalanceModel;
import com.xbongbong.saas.model.PayPlanModel;
import com.xbongbong.saas.model.PayPlanSheetModel;
import com.xbongbong.saas.model.PaymentBalanceModel;
import com.xbongbong.saas.model.PaymentModel;
import com.xbongbong.saas.model.PaymentSheetModel;
import com.xbongbong.saas.model.PurchaseInvoiceRelationshipModel;
import com.xbongbong.saas.model.PurchaseModel;
import com.xbongbong.saas.service.FundFixDataService;
import com.xbongbong.saas.service.RepairDataService;
import com.xbongbong.saas.service.RepairExplainService;
import com.xbongbong.saas.service.toolbox.help.FundMoneyHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.support.WriteRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author: 汪腾伟
 * @date: Created in 2021/7/6 10:09
 * @description：
 */
@Service("fundFixDataService")
public class FundFixDataServiceImpl implements FundFixDataService {

    private static final Logger LOG = LoggerFactory.getLogger(FundFixDataServiceImpl.class);

    @Resource
    private FundMoneyHelp fundMoneyHelp;
    @Resource
    private ContractModel contractModel;
    @Resource
    private RepairExplainService repairExplainService;
    @Resource
    private PaymentModel paymentModel;
    @Resource
    private PaymentSheetModel paymentSheetModel;
    @Resource
    private RepairDataService repairDataService;
    @Resource
    private PurchaseModel purchaseModel;
    @Resource
    private PurchaseInvoiceRelationshipModel purchaseInvoiceRelationshipModel;
    @Resource
    private SaasSaveHelp saasSaveHelp;
    @Resource
    private PayPlanModel payPlanModel;
    @Resource
    private PayPlanSheetModel payPlanSheetModel;
    @Resource
    private PaymentBalanceModel paymentBalanceModel;
    @Resource
    private PayBalanceModel payBalanceModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;

    /**
     * 同一家公司同一个业务校正按钮限制点击次数不能超过10次
     */
    private static final int DAY_CLICK_COUNT_NUM = 20;
    @Resource
    private PayAndPaymentSheetHelp payAndPaymentSheetHelp;


    /**
     * 资金修复数据的一个方法
     *
     * @param fundFixDataDTO
     * @return
     */
    @Override
    public FundFixDataVO fixData(FundFixDataDTO fundFixDataDTO) throws XbbException {
        //这边需要加一个限制，就是用户当天点击第一次的时候需要提示用户这个按钮一天只能点击5次，超过5次的时候也需要提示
        String key = fundFixDataDTO.getCorpid()+ "_" + fundFixDataDTO.getFixTag();
        Long currentNum = paasRedisHelper.getIncrNumByOne(RedisPrefixConstant.FIX_DATA, key, RedisConstant.LONG_DURATION);
        if (currentNum > DAY_CLICK_COUNT_NUM) {
            throw new XbbException(PaymentCorrectionErrorCodeEnum.API_ERROR_270002, String.format(PaymentCorrectionErrorCodeEnum.API_ERROR_270002.getMsg(), DAY_CLICK_COUNT_NUM));
        }

        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        if (CollectionsUtil.isEmpty(dataIdList) || dataIdList.size() > BasicConstant.ONE) {
            throw new XbbException(PaymentCorrectionErrorCodeEnum.API_ERROR_270001);
        }
        //这边打算对代码进行分层，但是也没必要用工厂、策略之类的方法，直接用switch...case这种简单判断进行分层
        String fixTag = fundFixDataDTO.getFixTag();
        FundFixTagEnum fundFixTagEnum = FundFixTagEnum.getFundFixTagByType(fixTag);
        FundFixDataVO fundFixDataVO = new FundFixDataVO();
        switch (fundFixTagEnum) {
            case CONTRACT_FIX_TAG:
                fundFixDataVO = fixContract(fundFixDataDTO);
                break;
            case CONTRACT_INVOICE_FIX_TAG:
                fundFixDataVO = fixContractInvoice(fundFixDataDTO);
                break;
            case PAYMENT_INVOICE_FIX_TAG:
                //核心，简单粗暴的做法就是根据回款计划id查询出合同id，然后更新这个合同、回款单、回款计划的开票与未开票金额。后面如果需要改造就只更新回款计划开票金额
                fundFixDataVO = fixPaymentInvoice(fundFixDataDTO);
                break;
            case PAYMENT_SHEET_INVOICE_FIX_TAG:
                //核心，简单粗暴做法就是根据回款单id查询出合同id，然后更新这个合同、回款单、回款计划的开票与未开票金额。后面如果需要改造的就只更新回款单的开票金额
                fundFixDataVO = fixPaymentSheetInvoice(fundFixDataDTO);
                break;
            case PAYMENT_FIX_TAG:
                fundFixDataVO = fixPayment(fundFixDataDTO);
                break;
            case PURCHASE_FIX_TAG:
                fundFixDataVO = fixPurchase(fundFixDataDTO);
                break;
            case PURCHASE_INVOICE_FIX_TAG:
            case PAY_INVOICE_FIX_TAG:
            case PAY_SHEET_INVOICE_FIX_TAG:
                fundFixDataVO = fixPurchaseInvoice(fundFixDataDTO);
                break;
            case PAY_FIX_TAG:
                fundFixDataVO = fixPay(fundFixDataDTO);
                break;
            case PREPAYMENT_BALANCE_FIX_TAG:
                fundFixDataVO = fixPrepaymentBalance(fundFixDataDTO);
                break;
            case PREPAY_BALANCE_FIX_TAG:
                fundFixDataVO = fixPrepayBalance(fundFixDataDTO);
                break;
            default:
                break;
        }
        return fundFixDataVO;
    }

    /**
     * 修复合同金额已收、未收、坏账金额，收款比例
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixContract(FundFixDataDTO fundFixDataDTO) throws XbbException{
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        Long contractId = fundFixDataDTO.getDataIdList().get(BasicConstant.ZERO);
        String corpid = fundFixDataDTO.getCorpid();
        FormDataUpdateGetDTO formDataUpdateGetDTO = new FormDataUpdateGetDTO();
        BeanUtil.copyProperties(fundFixDataDTO, formDataUpdateGetDTO);
        ContractEntityExt contractEntityExt = contractModel.getByKey(contractId, corpid);
        formDataUpdateGetDTO.setDataId(contractId);
        fixContractAmount(formDataUpdateGetDTO, contractId, corpid, contractEntityExt);
        return fundFixDataVO;
    }

    /**
     * 修复合同金额已收、未收、坏账金额，收款比例
     *
     * @param formDataUpdateGetDTO 入参
     * @param contractId 合同id
     * @param corpid 公司id
     * @param contract 合同实体
     * @throws XbbException
     */
    @Override
    public void fixContractAmount(FormDataUpdateGetDTO formDataUpdateGetDTO, Long contractId, String corpid, ContractEntityExt contract) throws XbbException {
        //正常回款单金额
        Double totalWriteOffAmount = fundMoneyHelp.getSheetTotalWriteOffAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO,true,true, PaymentSheetEnum.CONTRACT.getAttr());
        //(负的)红冲回款单金额
        Double redAmount = fundMoneyHelp.getSheetRedAndBadAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO, true, false, PaymentSheetEnum.CONTRACT.getAttr());
        //坏账金额
        Double badAmount = fundMoneyHelp.getSheetRedAndBadAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO, false, true, PaymentSheetEnum.CONTRACT.getAttr());
        LOG.info("统计出来的金额为：totalWriteOffAmount：{}, redAmount：{}， badAmount：{}", totalWriteOffAmount, redAmount, badAmount);
        List<UpdateDataEntity> updateDataEntityList = new ArrayList<>();
        Double finishedMoney = contract.getData().getDouble(ContractEnum.FINISH_AMOUNT.getAttr());
        Double unFinishedMoney = contract.getData().getDouble(ContractEnum.UN_FINISH_AMOUNT.getAttr());
        Double payPercent = contract.getData().getDouble(ContractEnum.PAY_PERCENT.getAttr());
        Double contractAmount = contract.getData().getDouble(ContractEnum.AMOUNT.getAttr());
        Double badMoney = contract.getData().getDouble(ContractEnum.BAD_AMOUNT.getAttr());
        // 正确的合同已收款
        Double rightFinished = Arith.add(totalWriteOffAmount, redAmount);
        // 正确的合同未收款
        /*
         * 合同金额=正常回款单金额-(正的)红冲回款单金额(实际入库的红冲金额是负数)+坏账金额+未收款金额
         * 即 合同金额=正常回款单金额+(负的)红冲回款单金额+坏账金额+未收款金额
         *
         * 未收款金额 = 合同金额 - ( 正常回款单金额+(负的)红冲回款单金额+坏账金额 ) = 合同金额 - 正常回款单金额 - (负的)红冲单金额 - 坏账金额
         */
        Double rightUnFinished = Arith.sub(Arith.sub(Arith.sub(contractAmount, totalWriteOffAmount),redAmount),badAmount);
        // 正确的合同收款比率
        Double rightPayPercent = 1D;
        if (Double.compare(contractAmount, 0) != 0) {
            rightPayPercent = Arith.div(rightFinished, contractAmount);
        }
        JSONObject updateData = new JSONObject();
        if (!Objects.equals(finishedMoney, rightFinished)) {
            updateData.put(ContractEnum.FINISH_AMOUNT.getAttr(), rightFinished);
        }
        if (!Objects.equals(unFinishedMoney, rightUnFinished)) {
            updateData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), rightUnFinished);
        }
        if (!Objects.equals(payPercent, rightPayPercent)) {
            updateData.put(ContractEnum.PAY_PERCENT.getAttr(), rightPayPercent);
        }
        if (!Objects.equals(badMoney, badAmount)) {
            updateData.put(ContractEnum.BAD_AMOUNT.getAttr(), badAmount);
        }
        if (!updateData.isEmpty()) {
            updateDataEntityList.add(ExplainUtil.getUpdateData(contractId, updateData, corpid));
            contractModel.updateBatch(updateDataEntityList, corpid);
        }
    }

    /**
     * 修复合同开票金额未开票金额
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixContractInvoice(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        String corpid = fundFixDataDTO.getCorpid();
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        RepairInvoiceAmountDTO repairInvoiceAmountDTO = new RepairInvoiceAmountDTO();
        repairInvoiceAmountDTO.setParamCorpid(corpid);
        repairInvoiceAmountDTO.setLinkIdList(dataIdList);
        repairExplainService.scriptInvoiceAmountData(repairInvoiceAmountDTO);
        return fundFixDataVO;
    }

    /**
     * 修复回款计划开票金额
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPaymentInvoice(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        String corpid = fundFixDataDTO.getCorpid();
        //回款计划（应收款）id
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        PaymentEntityExt paymentEntityExt = paymentModel.getByKey(dataIdList.get(BasicConstant.ZERO), corpid);
        JSONObject jsonObject = paymentEntityExt.getData();
        String contractId = jsonObject.getString(PaymentEnum.CONTRACT.getAttr());
        List<Long> linkIdList = new ArrayList<>(Arrays.asList(Long.parseLong(contractId)));
        RepairInvoiceAmountDTO repairInvoiceAmountDTO = new RepairInvoiceAmountDTO();
        repairInvoiceAmountDTO.setParamCorpid(corpid);
        repairInvoiceAmountDTO.setLinkIdList(linkIdList);
        repairExplainService.scriptInvoiceAmountData(repairInvoiceAmountDTO);
        return fundFixDataVO;
    }

    /**
     * 修复回款单开票金额
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPaymentSheetInvoice(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        String corpid = fundFixDataDTO.getCorpid();
        //回款单id
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        PaymentSheetEntityExt paymentSheetEntityExt = paymentSheetModel.getByKey(dataIdList.get(BasicConstant.ZERO), corpid);
        JSONObject jsonObject = paymentSheetEntityExt.getData();
        JSONArray contractJsonArray = jsonObject.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
        //为啥这一步要处理的这么麻烦，主要是现在数据库保存的格式不一定是字符串
        List<Long> linkIdList = new ArrayList<>();
        if (CollectionUtils.isEmpty(contractJsonArray)){
            return fundFixDataVO;
        }
        for (int i = BasicConstant.ZERO; i < contractJsonArray.size(); i++) {
            linkIdList.add(Long.valueOf(contractJsonArray.getString(i)));
        }
        RepairInvoiceAmountDTO repairInvoiceAmountDTO = new RepairInvoiceAmountDTO();
        repairInvoiceAmountDTO.setParamCorpid(corpid);
        repairInvoiceAmountDTO.setLinkIdList(linkIdList);
        repairExplainService.scriptInvoiceAmountData(repairInvoiceAmountDTO);
        return fundFixDataVO;
    }

    /**
     * 修正回款计划（应收款）的已收、未收、坏账金额、应收状态
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPayment(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        Long dataId = fundFixDataDTO.getDataIdList().get(BasicConstant.ZERO);
        String corpid = fundFixDataDTO.getCorpid();
        FormDataUpdateGetDTO formDataUpdateGetDTO = new FormDataUpdateGetDTO();
        BeanUtil.copyProperties(fundFixDataDTO, formDataUpdateGetDTO);
        formDataUpdateGetDTO.setDataId(dataId);
        PaymentEntityExt paymentEntityExt = paymentModel.getByKey(dataId, corpid);
        fixPaymentAmount(formDataUpdateGetDTO, dataId, corpid, paymentEntityExt);
        return fundFixDataVO;
    }

    /**
     * 修复回款计划金额数据，如已收、未收、坏账金额，回款状态
     *
     * @param formDataUpdateGetDTO
     * @param paymentId 回款计划id
     * @param corpid 公司id
     * @param payment 回款计划实体
     * @throws XbbException
     */
    @Override
    public void fixPaymentAmount(FormDataUpdateGetDTO formDataUpdateGetDTO, Long paymentId, String corpid, PaymentEntityExt payment) throws XbbException {
        //正常回款单金额
        Double totalWriteOffAmount = fundMoneyHelp.getSheetTotalWriteOffAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO,true,true,PaymentSheetEnum.PAYMENT.getAttr());
        //(负的)红冲回款单金额
        Double redAmount = fundMoneyHelp.getSheetRedAndBadAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO, true, false, PaymentSheetEnum.PAYMENT.getAttr());
        //坏账金额
        Double badAmount = fundMoneyHelp.getSheetRedAndBadAmount(XbbRefTypeEnum.PAYMENT_SHEET,formDataUpdateGetDTO, false, true, PaymentSheetEnum.PAYMENT.getAttr());

        List<UpdateDataEntity> updateDataEntityList = new ArrayList<>();
        Double planAmount = payment.getData().getDouble(PaymentEnum.AMOUNT.getAttr());
        Double finishedMoney = payment.getData().getDouble(PaymentEnum.REAL_AMOUNT.getAttr());
        Double unFinishedMoney = payment.getData().getDouble(PaymentEnum.UN_AMOUNT.getAttr());
        Double badMoney = payment.getData().getDouble(PaymentEnum.BAD_AMOUNT.getAttr());
        String planStatus = payment.getData().getString(PaymentEnum.STATUS.getAttr());
        // 正确的回款计划已收款
        Double rightFinished = Arith.add(totalWriteOffAmount, redAmount);
        // 正确的回款计划未收款
        /*
         * 回款计划金额=正常回款单金额-(正的)红冲回款单金额(实际入库的红冲金额是负数)+坏账金额+未收款金额
         * 即 回款计划金额=正常回款单金额+(负的)红冲回款单金额+坏账金额+未收款金额
         *
         * 未收款金额 = 回款计划金额 - ( 正常回款单金额+(负的)红冲回款单金额+坏账金额 ) = 回款计划金额 - 正常回款单金额 - (负的)红冲单金额 - 坏账金额 + 红冲金额(负的)
         */
        /**
         * 应收款的红冲金额
         */
        Double paymentRedAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(payment.getData(), PaymentEnum.RED_AMOUNT.getAttr(), 0D);
        Double rightUnFinished =Arith.add(Arith.sub(Arith.sub(Arith.sub(planAmount, totalWriteOffAmount),redAmount),badAmount), paymentRedAmount);
        JSONObject jsonObject = new JSONObject();
        if (!Objects.equals(finishedMoney, rightFinished)) {
            jsonObject.put(PaymentEnum.REAL_AMOUNT.getAttr(), rightFinished);
        }
        if (!Objects.equals(unFinishedMoney, rightUnFinished)) {
            jsonObject.put(PaymentEnum.UN_AMOUNT.getAttr(), rightUnFinished);
        }
        if (!Objects.equals(badMoney, badAmount)) {
            jsonObject.put(PaymentEnum.BAD_AMOUNT.getAttr(), badAmount);
        }
        String rightPlanStatus = payAndPaymentSheetHelp.handleStatus(planAmount, rightUnFinished, badAmount, paymentRedAmount);
        if (!Objects.equals(planStatus, rightPlanStatus)) {
            jsonObject.put(PaymentEnum.STATUS.getAttr(), rightPlanStatus);
        }
        if (!jsonObject.isEmpty()) {
            updateDataEntityList.add(ExplainUtil.getUpdateData(paymentId, jsonObject, corpid));
            paymentModel.updateBatch(updateDataEntityList, corpid);
        }
    }

    /**
     * 修正采购合同的已付、未付、坏账金额、付款比例
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPurchase(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        UpdateDataDTO updateDataDTO = new UpdateDataDTO();
        BeanUtil.copyProperties(fundFixDataDTO, updateDataDTO);
        updateDataDTO.setDataIds(dataIdList);
        repairDataService.purchaseFinishAmount(updateDataDTO);
        return fundFixDataVO;
    }

    /**
     * 修正采购合同/付款计划/付款单的开票金额、未开票金额。
     * 注意这边跟合同那边处理方式不一样，是因为写这个脚本的人已经根据不同业务去做过区分了。
     * 举例子：比如更新付款计划的开票金额，就去更新付款计划的开票金额。就不会先去找到采购合同，然后找出采购合同下面的所有付款计划、付款单，然后同步更新
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPurchaseInvoice(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        String fixTag = fundFixDataDTO.getFixTag();
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        PurchaseInvoiceRepairAmountDTO purchaseInvoiceRepairAmountDTO = new PurchaseInvoiceRepairAmountDTO();
        BeanUtil.copyProperties(fundFixDataDTO, purchaseInvoiceRepairAmountDTO);
        if (Objects.equals(fixTag, FundFixTagEnum.PURCHASE_INVOICE_FIX_TAG.getFixTag())) {
            purchaseInvoiceRepairAmountDTO.setModelType(BasicConstant.ONE);
        }else if (Objects.equals(fixTag, FundFixTagEnum.PAY_INVOICE_FIX_TAG.getFixTag())) {
            purchaseInvoiceRepairAmountDTO.setModelType(BasicConstant.TWO);
        }else if (Objects.equals(fixTag, FundFixTagEnum.PAY_SHEET_INVOICE_FIX_TAG.getFixTag())) {
            purchaseInvoiceRepairAmountDTO.setModelType(BasicConstant.THREE);
        }
        purchaseInvoiceRepairAmountDTO.setLinkIdList(dataIdList);
        repairPurchaseInvoiceAmount(purchaseInvoiceRepairAmountDTO);
        return fundFixDataVO;
    }

    /**
     * 修正采购合同/付款计划/付款单的开票金额、未开票金额
     *
     * @param purchaseInvoiceRepairAmountDTO
     * @throws Exception
     */
    @Override
    public void repairPurchaseInvoiceAmount(PurchaseInvoiceRepairAmountDTO purchaseInvoiceRepairAmountDTO) throws XbbException {
        String corpid = purchaseInvoiceRepairAmountDTO.getCorpid();
        Integer modelType = purchaseInvoiceRepairAmountDTO.getModelType();
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(ParameterConstant.CORPID,corpid);
        param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        List<Long> linkIdList = purchaseInvoiceRepairAmountDTO.getLinkIdList();
        List<PurchaseEntityExt> purchaseEntityExtList;
        if(Objects.equals(modelType, BasicConstant.ONE)) {
            if (CollectionsUtil.isNotEmpty(linkIdList)) {
                param.put("idIn", linkIdList);
                purchaseEntityExtList = purchaseModel.findEntitys(param);
                param.remove("idIn");
            } else {
                linkIdList = new ArrayList<>();
                purchaseEntityExtList = purchaseModel.findEntitys(param);
                if (CollectionsUtil.isNotEmpty(purchaseEntityExtList)) {
                    for (PurchaseEntityExt purchaseEntityExt : purchaseEntityExtList) {
                        Long id = purchaseEntityExt.getId();
                        linkIdList.add(id);
                    }
                }
            }
            param.put("purchaseIdIn", linkIdList);
            param.put("isCancel", BasicConstant.ZERO);
            List<PurchaseInvoiceRelationshipEntity> purchaseInvoiceRelationshipEntityList = purchaseInvoiceRelationshipModel.findEntitys(param);
            Map<Long, Double> purchaseInvoiceAmountMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (CollectionsUtil.isNotEmpty(purchaseInvoiceRelationshipEntityList)) {
                for (PurchaseInvoiceRelationshipEntity purchaseInvoiceRelationshipEntity : purchaseInvoiceRelationshipEntityList) {
                    saasSaveHelp.addMapNormal(purchaseInvoiceAmountMap, purchaseInvoiceRelationshipEntity.getPurchaseId(), purchaseInvoiceRelationshipEntity.getMoney());
                }
            }
            List<UpdateDataEntity> purchaseUpdateList = new ArrayList<>();
            if (CollectionsUtil.isNotEmpty(purchaseEntityExtList)) {
                for (PurchaseEntityExt purchaseEntityExt : purchaseEntityExtList) {
                    Long id = purchaseEntityExt.getId();
                    if(purchaseInvoiceAmountMap.containsKey(id)) {
                        JSONObject data = purchaseEntityExt.getData();
                        double totalMoney = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PurchaseEnum.TOTAL_MONEY.getAttr(), 0D);
                        double invoiceAmount = purchaseInvoiceAmountMap.getOrDefault(id, 0D);
                        data.put(PurchaseEnum.INVOICE_AMOUNT.getAttr(), invoiceAmount);
                        data.put(PurchaseEnum.UN_INVOICE_AMOUNT.getAttr(), Arith.sub(totalMoney, invoiceAmount));
                        UpdateDataEntity purchaseUpdateEntity = ExplainUtil.getUpdateData(id, data, corpid);
                        purchaseUpdateEntity.setUpdateTime(purchaseEntityExt.getUpdateTime());
                        purchaseUpdateList.add(purchaseUpdateEntity);
                    }
                }
            }
            if (CollectionsUtil.isNotEmpty(purchaseUpdateList)) {
                purchaseModel.updateBatch(purchaseUpdateList, corpid, false);
            }
        }
        if(Objects.equals(modelType, BasicConstant.TWO)) {
            Map<Long, Double> payPlanInvoiceAmountMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<PayPlanEntityExt> payPlanEntityExtList;
            if (CollectionsUtil.isNotEmpty(linkIdList)) {
                param.put("idIn", linkIdList);
                payPlanEntityExtList = payPlanModel.findEntitys(param);
                param.remove("idIn");
            } else {
                linkIdList = new ArrayList<>();
                payPlanEntityExtList = payPlanModel.findEntitys(param);
                if (CollectionsUtil.isNotEmpty(payPlanEntityExtList)) {
                    for (PayPlanEntityExt payPlanEntityExt : payPlanEntityExtList) {
                        Long id = payPlanEntityExt.getId();
                        linkIdList.add(id);
                    }
                }
            }

            param.put("payPlanIdIn", linkIdList);
            param.put("isCancel", BasicConstant.ZERO);
            List<PurchaseInvoiceRelationshipEntity> purchaseInvoiceRelationshipEntityList = purchaseInvoiceRelationshipModel.findEntitys(param);
            if (CollectionsUtil.isNotEmpty(purchaseInvoiceRelationshipEntityList)) {
                for (PurchaseInvoiceRelationshipEntity purchaseInvoiceRelationshipEntity : purchaseInvoiceRelationshipEntityList) {
                    saasSaveHelp.addMapNormal(payPlanInvoiceAmountMap, purchaseInvoiceRelationshipEntity.getPayPlanId(), purchaseInvoiceRelationshipEntity.getMoney());
                }
            }
            List<UpdateDataEntity> payPlanUpdateList = new ArrayList<>();
            if (CollectionsUtil.isNotEmpty(payPlanEntityExtList)) {
                for (PayPlanEntityExt payPlanEntityExt : payPlanEntityExtList) {
                    Long id = payPlanEntityExt.getId();
                    if(payPlanInvoiceAmountMap.containsKey(id)) {
                        JSONObject data = payPlanEntityExt.getData();
                        double invoiceAmount = payPlanInvoiceAmountMap.getOrDefault(id, 0D);
                        data.put(PayPlanEnum.INVOICE_AMOUNT.getAttr(), invoiceAmount);
                        UpdateDataEntity payPlanUpdateEntity = ExplainUtil.getUpdateData(id, data, corpid);
                        payPlanUpdateEntity.setUpdateTime(payPlanEntityExt.getUpdateTime());
                        payPlanUpdateList.add(payPlanUpdateEntity);
                    }
                }
            }
            if (CollectionsUtil.isNotEmpty(payPlanUpdateList)) {
                payPlanModel.updateBatch(payPlanUpdateList, corpid, WriteRequest.RefreshPolicy.NONE);
            }
        }
        if(Objects.equals(modelType, BasicConstant.THREE)){
            Map<Long,Double> paySheetInvoiceAmountMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<PayPlanSheetEntityExt> paySheetEntityExtList;
            if (CollectionsUtil.isNotEmpty(linkIdList)) {
                param.put("idIn", linkIdList);
                paySheetEntityExtList = payPlanSheetModel.findEntitys(param);
                param.remove("idIn");
            }  else {
                paySheetEntityExtList = payPlanSheetModel.findEntitys(param);
                if(CollectionsUtil.isNotEmpty(paySheetEntityExtList)){
                    for (PayPlanSheetEntityExt paySheetEntityExt : paySheetEntityExtList) {
                        Long id = paySheetEntityExt.getId();
                        linkIdList.add(id);
                    }
                }
            }
            param.put("paySheetIdIn",linkIdList);
            param.put("isCancel",BasicConstant.ZERO);
            List<PurchaseInvoiceRelationshipEntity> purchaseInvoiceRelationshipEntityList = purchaseInvoiceRelationshipModel.findEntitys(param);
            if (CollectionsUtil.isNotEmpty(purchaseInvoiceRelationshipEntityList)) {
                for (PurchaseInvoiceRelationshipEntity purchaseInvoiceRelationshipEntity : purchaseInvoiceRelationshipEntityList) {
                    saasSaveHelp.addMapNormal(paySheetInvoiceAmountMap, purchaseInvoiceRelationshipEntity.getPaySheetId(), purchaseInvoiceRelationshipEntity.getMoney());
                }
            }
            List<UpdateDataEntity> paySheetUpdateList = new ArrayList<>();
            if (CollectionsUtil.isNotEmpty(paySheetEntityExtList)) {
                for (PayPlanSheetEntityExt paySheetEntityExt : paySheetEntityExtList) {
                    Long id = paySheetEntityExt.getId();
                    if(paySheetInvoiceAmountMap.containsKey(id)) {
                        JSONObject data = paySheetEntityExt.getData();
                        double invoiceAmount = paySheetInvoiceAmountMap.getOrDefault(id, 0D);
                        data.put(PaySheetEnum.INVOICE_AMOUNT.getAttr(), invoiceAmount);
                        UpdateDataEntity paySheetUpdateEntity = ExplainUtil.getUpdateData(id, data, corpid);
                        paySheetUpdateEntity.setUpdateTime(paySheetEntityExt.getUpdateTime());
                        paySheetUpdateList.add(paySheetUpdateEntity);
                    }
                }
            }
            if (CollectionsUtil.isNotEmpty(paySheetUpdateList)) {
                payPlanSheetModel.updateBatch(paySheetUpdateList, corpid, WriteRequest.RefreshPolicy.NONE);
            }
        }
    }

    @Override
    public String repairPrepaymentBalance(FundFixDataDTO fundFixDataDTO) throws XbbException {
        String corpid = fundFixDataDTO.getCorpid();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("orderByStr", "id");
        params.put("del", 0);
        params.put("pageSize", 1000);
        params.put("corpid", corpid);
        params.put("flowStatusIn", FlowStatusEnum.getStatisticType());
        List<Long> linkIdList = fundFixDataDTO.getDataIdList();
        if (CollectionsUtil.isNotEmpty(linkIdList)) {
            params.put("customerIdIn", linkIdList);
        }
        List<PaymentSheetEntityExt> sheetList = new ArrayList<>();
        while (true) {
            List<PaymentSheetEntityExt> list = paymentSheetModel.findAffectBalanceEntitys(params);
            if (list == null || list.isEmpty()) {
                break;
            }
            PaymentSheetEntityExt paymentSheetEntityExt = list.get(list.size() - 1);
            params.put("idGte", paymentSheetEntityExt.getId());
            sheetList.addAll(list);
        }
        Map<Long, Double> linkIdBalanceMap = new HashMap<>(sheetList.size());
        Set<Long> linkIdSet = new HashSet<>();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.PAYMENT_SHEET;
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(xbbRefTypeEnum);
        for (PaymentSheetEntityExt entityExt : sheetList) {
            //汇总回款单得到客户的最终预收款余额
            packageBalance(linkIdBalanceMap, linkIdSet, xbbRefTypeEnum, sheetAttrPojo, entityExt);
        }
        StringBuilder errorCids = new StringBuilder();
        List<Long> linkIdIn = new ArrayList<>(linkIdSet);
        int size = linkIdIn.size();
        for (int i = 0; i < size; i+= EsNormalConstant.INSERT_NUM) {
            int toIndex = i +  EsNormalConstant.INSERT_NUM;
            if (toIndex > size) {
                toIndex = size;
            }
            List<Long> newList = linkIdIn.subList(i, toIndex);
            if (newList.isEmpty()) {
                continue;
            }
            List<PaymentBalanceEntity> balanceList = paymentBalanceModel.getByCustomerIds(newList, corpid);
            List<PaymentBalanceEntity> updateList = new ArrayList<>();
            for (PaymentBalanceEntity balanceEntity : balanceList) {
                Long customerId = balanceEntity.getCustomerId();
                Double total = linkIdBalanceMap.getOrDefault(customerId, 0D);
                if (total < 0) {
                    if (errorCids.length() > 0) {
                        errorCids.append(SymbolConstant.COMMA_ENGLISH);
                    }
                    errorCids.append(customerId);
                    continue;
                }
                if (Objects.equals(total, balanceEntity.getAdvancePaymentBalance())) {
                    continue;
                }
                balanceEntity.setAdvancePaymentBalance(total);
                updateList.add(balanceEntity);
            }
            if (updateList.size() > 0) {
                paymentBalanceModel.updateBatchAdvance(corpid, updateList);
            }
        }
        String msg = "success";
        if (errorCids.length() > 0) {
            errorCids.insert(0, "更新失败的客户id集合（更新后余额小于0）：");
            msg = errorCids.toString();
            LOG.error(msg);
        }
        return msg;
    }

    @Override
    public String repairPrepayBalance(FundFixDataDTO fundFixDataDTO) throws XbbException {
        String corpid = fundFixDataDTO.getCorpid();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("orderByStr", "id");
        params.put("del", 0);
        params.put("pageSize", 1000);
        params.put("corpid", corpid);
        params.put("flowStatusIn", FlowStatusEnum.getStatisticType());
        List<Long> linkIdList = fundFixDataDTO.getDataIdList();
        if (CollectionsUtil.isNotEmpty(linkIdList)) {
            params.put("supplierIdIn", linkIdList);
        }
        List<PayPlanSheetEntityExt> sheetList = new ArrayList<>();
        while (true) {
            List<PayPlanSheetEntityExt> list = payPlanSheetModel.findAffectBalanceEntitys(params);
            if (list == null || list.isEmpty()) {
                break;
            }
            PayPlanSheetEntityExt paymentSheetEntityExt = list.get(list.size() - 1);
            params.put("idGte", paymentSheetEntityExt.getId());
            sheetList.addAll(list);
        }
        Map<Long, Double> linkIdBalanceMap = new HashMap<>(sheetList.size());
        Set<Long> linkIdSet = new HashSet<>();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.PAY_SHEET;
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(xbbRefTypeEnum);
        for (PayPlanSheetEntityExt entityExt : sheetList) {
            //汇总回款单得到经销商的最终预付款余额
            packageBalance(linkIdBalanceMap, linkIdSet, xbbRefTypeEnum, sheetAttrPojo, entityExt);
        }
        StringBuilder errorCids = new StringBuilder();
        List<Long> linkIdIn = new ArrayList<>(linkIdSet);
        int size = linkIdIn.size();
        for (int i = 0; i < size; i+=EsNormalConstant.INSERT_NUM) {
            int toIndex = i +  EsNormalConstant.INSERT_NUM;
            if (toIndex > size) {
                toIndex = size;
            }
            List<Long> newList = linkIdIn.subList(i, toIndex);
            if (newList.isEmpty()) {
                continue;
            }
            List<PayBalanceEntity> balanceList = payBalanceModel.getBySupplierIds(newList, corpid);
            List<PayBalanceEntity> updateList = new ArrayList<>();
            for (PayBalanceEntity balanceEntity : balanceList) {
                Long supplierId = balanceEntity.getSupplierId();
                Double total = linkIdBalanceMap.getOrDefault(supplierId, 0D);
                if (total < 0) {
                    if (errorCids.length() > 0) {
                        errorCids.append(SymbolConstant.COMMA_ENGLISH);
                    }
                    errorCids.append(supplierId);
                    continue;
                }
                if (Objects.equals(total, balanceEntity.getAdvancePayBalance())) {
                    continue;
                }
                balanceEntity.setAdvancePayBalance(total);
                updateList.add(balanceEntity);
            }
            if (updateList.size() > 0) {
                payBalanceModel.updateBatchAdvance(corpid, updateList);
            }
        }
        String msg = "success";
        if (errorCids.length() > 0) {
            errorCids.insert(0, "更新失败的供应商id集合（更新后余额小于0）：");
            msg = errorCids.toString();
            LOG.error(msg);
        }
        return msg;
    }

    /**
     * 修正付款计划的已付、未付、坏账金额，以及付款状态
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPay(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        List<Long> dataIdList = fundFixDataDTO.getDataIdList();
        UpdateDataDTO updateDataDTO = new UpdateDataDTO();
        BeanUtil.copyProperties(fundFixDataDTO, updateDataDTO);
        updateDataDTO.setDataIds(dataIdList);
        repairDataService.payPlanFinishAmount(updateDataDTO);
        return fundFixDataVO;
    }

    /**
     * 修正预收款余额
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPrepaymentBalance(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        repairPrepaymentBalance(fundFixDataDTO);
        return fundFixDataVO;
    }

    /**
     * 修正预付款余额
     *
     * @param fundFixDataDTO
     * @return
     */
    private FundFixDataVO fixPrepayBalance(FundFixDataDTO fundFixDataDTO) throws XbbException {
        FundFixDataVO fundFixDataVO = new FundFixDataVO(Boolean.TRUE);
        repairPrepayBalance(fundFixDataDTO);
        return fundFixDataVO;
    }


    /**
     * 汇总回款单得到客户的最终预收/付款余额
     * @param linkIdBalanceMap 关联客户/供应商id - 其最终预收/付款余额 对应关系map
     * @param xbbRefTypeEnum 业务类型，回款单or付款单
     * @param sheetAttrPojo 字段参数pojo
     * @param entityExt 回、付款单实体
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void packageBalance(Map<Long, Double> linkIdBalanceMap, Set<Long> linkIdIn, XbbRefTypeEnum xbbRefTypeEnum, SheetAttrPojo sheetAttrPojo, PaasFormDataEntityExt entityExt) {
        JSONObject data = JsonHelperUtil.getJSONObject(entityExt.getData());
        String sheetType = data.getString(sheetAttrPojo.getAttrSheetType());
        SheetTypeEnum sheetTypeEnum = SheetTypeEnum.getByCodeRefType(sheetType, xbbRefTypeEnum.getCode());
        Double balance = 0D;
        switch (sheetTypeEnum) {
            case PREPAYMENT:
            case PREPAY:
            case RED_PREPAYMENT:
            case RED_PREPAY:
                //红冲预收/付款存储的是负值可直接使用
                balance = data.getDoubleValue(sheetAttrPojo.getAttrAmount());
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
            case RED_WRITE_OFF_BALANCE:
                balance = data.getDoubleValue(sheetAttrPojo.getAttrWriteOffAmount());
                //核销金额要用余额减去（红冲预收款核销退到余额是负数）
                balance = Arith.mul(balance, -1D);
                break;
            default:
                break;
        }
        //关联客户id、关联供应商id
        Long linkId = data.getLong(sheetAttrPojo.getAttrCustomer());
        Double total = linkIdBalanceMap.getOrDefault(linkId, 0D);
        total = Arith.add(total, balance);
        linkIdBalanceMap.put(linkId, total);
        linkIdIn.add(linkId);
    }
}
